Terraform v0.9→v0.11までの変更点をまとめてみた
はじめに
こんにちは、佐伯です。
私自身がTerraformのバージョンアップについていけてなかったので、キャッチアップすべくいっきにv0.9からv0.11までの大きな変更をざっくり追ってみたいと思います。v0.9からなのはこちらでv0.8の紹介をしているためです。なお、プロバイダーごとの変更などは省いており、Terraform本体の機能についての変更を挙げています。
0.9.0 (March 15, 2017)
Remote Backends
v0.8では、Terraform管理対象リソースの状態を保存しているtfstateファイルをリモートに保存するためにterraform remote config
でバックエンドの設定をしていました。v0.9からはremote
サブコマンドが廃止され、Terraformファイルで設定できるようになりました。
以下はS3にtfstateを保存する場合の例です。
v0.8:
v0.8までは初回にterraformn remote config
でバックエンドの設定を行っていました。しかしTerraformファイルに書くことができず、メンバーやプロジェクトが増えるたびに「バックエンドの設定どうやるんだっけ?」とハマることが結構ありました。
$ terraform remote config \ -backend=S3 \ -backend-config="bucket=terraform-tfstate-bucket" \ -backend-config="key=tfstate" Remote configuration updated Remote state configured and pulled.
v0.9:
v0.9からは以下のようにTerraformファイルにバックエンドの設定ができるようになり、設定手順などを別途用意する必要がなくなりました。
$ cat backend.tf terraform { backend "s3" { bucket = "terraform-tfstate-bucket" key = "tfstate" region = "ap-northeast-1" } } # terraform initで初期化または既存のtfstateと同期する $ terraform init Initializing the backend... Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes.
State Locking
複数の環境から同時にterraform apply
を実行してしまうと、意図せぬ変更になったりtfstateを壊してしまう可能性があるため、plan
やapply
を実行してる間、tfstateをロックする機能が追加されました。以下はS3をバックエンドにしてい場合の例です。S3がバックエンドの場合はDynamoDBを利用してState Lockを実装しています。
まず、State Lockの設定をする前にDynamoDBを作成しなければならないので、以下の設定でDynamoDBを作成します。RCU/WCUは1としています。
resource "aws_dynamodb_table" "terraform-state-lock" { name = "terraform-state-lock" read_capacity = 1 write_capacity = 1 hash_key = "LockID" attribute { name = "LockID" type = "S" } }
バックエンドの設定にlock_table
を追加します。値は先ほど作成したDynamoDBのテーブル名です。(v0.9.7からlock_table
はDeprecated、代わりにdynamodb_table
で設定します)
terraform { backend "s3" { bucket = "terraform-tfstate-bucket" key = "tfstate" region = "ap-northeast-1" lock_table = "terraform-state-lock" } }
State Lockの設定を追加すると、バックエンド設定を変更したらterraform init
せよとエラーメッセージが出力されます。
$ terraform plan Backend reinitialization required. Please run "terraform init". Reason: Backend configuration changed for "s3" The "backend" is the interface that Terraform uses to store state, perform operations, etc. If this message is showing up, it means that the Terraform configuration you're using is using a custom configuration for the Terraform backend. Changes to backend configurations require reinitialization. This allows Terraform to setup the new configuration, copy existing state, etc. This is only done during "terraform init". Please run that command now then try again. If the change reason above is incorrect, please verify your configuration hasn't changed and try again. At this point, no changes to your existing configuration or state have been made. Failed to load backend: Initialization required. Please see the error message above.
素直にterraform init
を実行します。tfstateを新しいバックエンドにコピーする?と聞かれますが、今回はバケットもキーも変更していない同じバックエンドでありコピーする必要がないのでno
を選択しました。
$ terraform init Initializing the backend... Backend configuration changed! Terraform has detected that the configuration specified for the backend has changed. Terraform will now reconfigure for this backend. If you didn't intend to reconfigure your backend please undo any changes to the "backend" section in your Terraform configuration. Do you want to copy the state from "s3"? Would you like to copy the state from your prior backend "s3" to the newly configured "s3" backend? If you're reconfiguring the same backend, answering "yes" or "no" shouldn't make a difference. Please answer exactly "yes" or "no". Enter a value: no Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your environment. If you forget, other commands will detect it and remind you to do so if necessary.
以上でState Lockの設定は完了です。plan
やapply
を実行するとDynamoDBのLockIDとInfoに下記のようなレコードが追加され、ロック中はレコードが存在しplan
やapply
が実行できません。アンロック時にレコードが削除される仕組みです。
なお、ロック中にplan
やapply
を実行すると以下のようなエラーメッセージが出力されます。ユーザーとホスト名出力されるのはわかりやすいですね。
$ terraform plan Error locking state: Error acquiring the state lock: ConditionalCheckFailedException: The conditional request failed status code: 400, request id: R0UOE0SCP1IJ4NDCM6K6FMIO1BVV4KQNSO5AEMVJF66Q9ASUAAJG Lock Info: ID: 6959406a-d096-173c-c9df-130d4a0c879d Path: terraform-tfstate-bucket/tfstate Operation: OperationTypePlan Who: saiki.ko@HL00245.local Version: 0.9.0 Created: 2017-11-20 10:13:42.610607 +0000 UTC Info: Terraform acquires a state lock to protect the state from being written by multiple users at the same time. Please resolve the issue above and try again. For most commands, you can disable locking with the "-lock=false" flag, but this is not recommended.
Destroy Provisioners
Provisionersがリソースの削除時にも実行できるようになりました。Provisioner自体使ったことないので具体的にこんなときに便利だよ!っていう具体的なユースケースが思いつきませんでした。すみません…。
0.10.0 (August 2, 2017)
Separate Provider Releases
AWS, GCP, Azureなどの各プロバイダーがTerraformコアから分割され、プラグイン形式で提供されるようになりました。それに伴いGitHubリポジトリも分割されています。
Automatic Provider Installation
プロバイダーのプラグイン形式での提供に伴い、terraform init
でプロバイダープラグインがカレントディレクトリの.terraform/plugins配下にダウンロードされるようになりました。
なお、v0.10.7のアップデートでプラグインキャッシュディレクトリを設定できるようになり、何度も同じプロバイダープラグインをダウンロードする必要がなくなりました。以下のように$HOME/.terraformrc
または環境変数に設定をすることができます。
$HOME/.terraformrc:
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
環境変数:
export TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache"
プラグインキャッシュディレクトリを有効にすると、terraform init
実行時にプラグイン配布サーバーから使用可能なプラグインの情報を取得します。プラグインのバージョンが選択されると、プラグイン配布サーバーからダウンロードするのではなく、まずプラグインキャッシュディレクトリに当該プラグインが存在しているかを確認し、存在していればそのプラグインをカレントディレクトリの.terraform/plugins配下にコピーして使用します。存在していなければプラグインキャッシュディレクトリにダウンロード後、カレントディレクトリの.terraform/plugins配下にコピーします。
CI/CDなどでTerraformを使用している場合、便利な機能だと思います。
Provider Constraints
こちらも同じくプロバイダーのプラグイン形式での提供に伴い、プロバイダープラグインのバージョンを設定ファイルで指定することができます。以下はAWSプロバイダープラグインのバージョンを1.3.1に固定しています。~> 1.0
と設定し、1.0以上といった指定をすることもできます。
provider "aws" { version = "1.3.1" }
State Environments
State Environmentsはv0.9のアップデートで追加された機能ですが、v0.10でサブコマンドがenv
からworkspace
にリネームされたので、こちらに記載したいと思います。
これまでProduction, Staging, Developなど環境ごとにリソースを作成する場合、ディレクトリを環境ごとに分割しリソースを管理していました。v0.9からディレクトリを分割せずに複数環境を作成することができるようになりました。
workspaceはデフォルトだとdefaultが選択されています。
$ terraform workspace list * default
terraform workspace new
で新しいworkspaceを作成してみます。
$ terraform workspace new prod Created and switched to workspace "prod"! You're now on a new, empty workspace. Workspaces isolate their state, so if you run "terraform plan" Terraform will not see any existing state for this configuration.
新しくworkspaceを作成すると作成したworkspaceが選択された状態になります。
$ terraform workspace list default * prod
workspaceを作成したタイミングでバックエンドにenv:/prod/tfstateが作成されています。
$ aws s3 ls s3://terraform-tfstate-bucket/env:/prod/tfstate 2017-11-21 14:58:32 282 tfstate
上記の様にenv:<workspace name>
をバックエンド設定に追加して、tfstateを環境ごとにわけているようです。なお、今回はバックエンドをS3に設定している場合の例ですが、バックエンド設定を行っていない場合は.terraform.tfstate.d/に環境ごとのtfstateが保存されます。
しかし、ベストプラクティスには次ような記述がありました。
Workspaces can be used to manage small differences between development, staging, and production, but they should not be treated as the only isolation mechanism. As Terraform configurations get larger, it's much more manageable and safer to split one large configuration into many smaller ones linked together with the terraform_remote_state data source. This allows teams to delegate ownership and reduce the potential impact of changes. For each smaller configuration, you can use workspaces to model the differences between development, staging, and production. However, if you have one large Terraform configuration, it is riskier and not recommended to use workspaces to handle those differences.
大きな構成をworkspaceだけで分割すると管理対象リソース増えて大変だし、リソースが増えてくるとProductionにはこれが必要だけどStagingには必要なくて、Productionには不要だけどStaging, Developには必要みたいなことが多くなってくるのでworkspaceのみで環境の分離を実現するのではなく、小さな構成ごとにTerraformファイルを分割し、terraform_remote_stateデータソースを使用してtfstateをリンクするような形がいいよ!ということだと解釈しました。
terraform_remote_stateデータソースについては後日調べたうえでプログに書きたいと思います。なお、すでにworkspaceとterraform_remote_stateを使用したベストプラクティス構成を考えている方がいらっしゃったので、後日参考に試してみたいと思います。
0.11.0 (November 16, 2017)
CHANGELOGにそれっぽいタイトルがなくて、適当にタイトルをつけてますのでご了承ください。
モジュールバージョン設定の追加
v0.10.6からTerraformモジュールをTerraform Module Registryよりインストールして利用できるようになりました。v0.11.0からはTerraform Module Registryで公開されているモジュールバージョンの指定ができるようになりました。
Terraform v0.11.0でterraform-aws-modules/vpc/awsを使用してみます。
module "vpc" { source = "terraform-aws-modules/vpc/aws" name = "my-vpc" cidr = "10.0.0.0/16" azs = ["ap-northeast-1a", "ap-northeast-1c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] tags = { Terraform = "true" Environment = "dev" } }
上記設定でterraform init
を実行すると1.3.0がダウンロードされました。
$ terraform init Initializing modules... - module.vpc Found version 1.3.0 of terraform-aws-modules/vpc/aws on registry.terraform.io Getting source "terraform-aws-modules/vpc/aws"
バージョンを1.0.0に固定しました。
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "1.0.0" name = "my-vpc" cidr = "10.0.0.0/16" azs = ["ap-northeast-1a", "ap-northeast-1c"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] tags = { Terraform = "true" Environment = "dev" } }
指定したバージョンがダウンロードされています。
$ terraform init Initializing modules... - module.vpc Found version 1.0.0 of terraform-aws-modules/vpc/aws on registry.terraform.io Getting source "terraform-aws-modules/vpc/aws"
モジュールバージョンの詳しい指定方法についてはこちらに記載があります。(なお、terraform-aws-modules/vpc/aws 1.0.0だとUsage通りの設定では動きませんでした…)
モジュールのプロバイダ設定継承ルールの変更
これまでマルチプロバイダー設定をしていた場合、モジュールのプロバイダー設定は呼び出し元のプロバイダー設定が継承され上書きされていました。v0.11.0からはエイリアスで追加したプロバイダー設定をモジュールで使用する場合は明示的にモジュールへ渡す必要があるとのことです。
provider "aws" { region = "ap-northeast-1" } provider "aws" { alias = "virginia" region = "us-east-1" } module "example" { source = "./example" providers = { aws = "aws.virginia" } }
Applyはデフォルトで対話形式に変更
以前からterraform plan
の-out
オプションは存在していましたが、terraform apply
時にplanで出力したファイルを指定しないとyes or noの入力を求められるようになりました。
$ terraform apply ~省略~ Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:
$ terraform apply "<path/to/file>" ~省略~ Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
terraform.tfstate.backupは作成されなくなる
リモートバックエンド設定時、ローカルのterraform.tfstate.backupは作成されなくなるので、各バックエンド側の機能でなんとかしてね!とのことです。S3の場合はバージョニング機能があるのでS3をリモートバックエンドに設定する際は必ず有効にしましょう。
アップグレードガイド
なお、Terraformのアップグレードを行う場合はアップグレードガイドがありますのでご確認ください。
最後に
全ての変更を拾えていないですが、いろいろなアップデートがあり纏めるのに時間がかかりました。また、拙い英語力で試しながら確認したので誤り等あればご指摘ください。